package com.onpositive.queryeditor.util;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;

import com.onpositive.commons.ui.dialogs.InputElementDialog;
import com.onpositive.semantic.model.binding.Binding;
import com.onpositive.semantic.model.realm.GroupingPointProvider;
import com.onpositive.semantic.model.realm.HashDelta;
import com.onpositive.semantic.model.realm.Realm;
import com.onpositive.semantic.model.tree.ITreeNode;
import com.onpositive.semantic.model.tree.LeafNode;
import com.onpositive.semantic.model.tree.RealmNode;
import com.onpositive.semantic.model.ui.property.editors.CompositeEditor;
import com.onpositive.semantic.model.ui.property.editors.DisposeBindingListener;
import com.onpositive.semantic.model.ui.property.editors.structured.ListEnumeratedValueSelector;
import com.onpositive.semantic.model.ui.property.editors.structured.UniversalLabelProvider;

public abstract class SelectionUtils
{

	String title;
	String description;

	public SelectionUtils(String title, String description)
	{
		super();
		this.title = title;
		this.description = description;
	}

	public abstract boolean isDisposed();

	@SuppressWarnings("rawtypes")
	protected abstract Collection getContents();

	public void doConfig()
	{

		CompositeEditor ed = new CompositeEditor();
		final LinkedHashSet<?> value = new LinkedHashSet<Object>(getContents());
		final LinkedHashSet<?> initialValue = new LinkedHashSet<Object>(getContents());
		final Binding bnd2 = new Binding(value);
		bnd2.setName(title);
		bnd2.setDescription(description);
		ed.setBinding(bnd2);
		final ListEnumeratedValueSelector<?> element = new ListEnumeratedValueSelector<Object>()
		{
			
			protected void configureViewer(TreeViewer newCheckList)
			{
				super.configureViewer(newCheckList);
				CheckboxTreeViewer t = (CheckboxTreeViewer) newCheckList;
				t.addTreeListener(new ITreeViewerListener()
				{
					
					public void treeExpanded(TreeExpansionEvent event)
					{
						 Object element2 = event.getElement();
						 if (element2 instanceof RealmNode){
							 RealmNode<?> n=(RealmNode<?>) element2;
							 initialValue.removeAll(n.getContents());
						 }
					}
					
					public void treeCollapsed(TreeExpansionEvent event)
					{
						
					}
				});
				t.addCheckStateListener(new ICheckStateListener()
				{

					public void checkStateChanged(CheckStateChangedEvent event)
					{
						Display.getCurrent().asyncExec(new Runnable()
						{

							public void run()
							{
								CheckboxTreeViewer t = (CheckboxTreeViewer) getViewer();
								if (isDisposed())
								{
									return;
								}
								Object value = t.getCheckedElements();

								Collection<Object> cValue = null;
								if (value instanceof Object[])
								{
									cValue = Arrays.asList((Object[]) value);
								} else
								{
									if (value != null)
									{
										cValue = Collections.singleton(value);
									} else
									{
										cValue = Collections.emptySet();
									}
								}
								HashSet<Object> aV = new HashSet<Object>();
								for (Object object : cValue)
								{
									if (object instanceof LeafNode<?>)
									{
										LeafNode<?> m = (LeafNode<?>) object;
										aV.add(m.getElement());
									} else
									{

										if (object instanceof RealmNode<?>)
										{
											RealmNode<?> node = (RealmNode<?>) object;
											ITreeNode<?>[] children = node.getChildren();
											if (!t.getGrayed(node))
											{
												for (ITreeNode<?> m : children)
												{
													aV.add(m.getElement());
												}
											}
											else
											{
												
												for (ITreeNode<?> m : children)
												{
													if (initialValue.contains(m.getElement())){
														aV.add(m.getElement());
													}
													else if (t.getChecked(m))
													{
														aV.add(m.getElement());
													}
												}
											}
										} else
										{
											aV.add(object);
										}
									}
								}
								HashDelta<Object> valueElements = HashDelta.buildFrom(getContents(), (Collection<Object>) aV);
								for (Object c : valueElements.getAddedElements())
								{
									doAdd(c);
								}
								for (Object m : valueElements.getRemovedElements())
								{
									doRemove(m);
								}
								onChange();
							}
						});

					}
				});
			}
		};
		element.setLabelProvider(new UniversalLabelProvider<Object>()
		{
			
			public Image getImage(Object element)
			{
				if (element instanceof String)
				{

				}
				return super.getImage(element);
			}
		});
		element.setAsCheckBox(true);
		element.setAsTree(true);
		element.setClusterizationPointProviders(null, new GroupingPointProvider<Object>()
		{

			
			public Set<? extends Object> getGroup(Object o)
			{
				return getElementGroup(o);
			}

			
			public Object getPresentationObject(Object o)
			{
				return o;
			}
		});
		bnd2.setRealm(new Realm<Object>(getAllowedElements()));
		element.setBinding(bnd2);
		ed.add(element);
		element.getLayoutHints().setHint(new Point(300, 400));
		element.setValue(getContents());
		DisposeBindingListener.linkBindingLifeCycle(bnd2, ed);
		InputElementDialog dlg = new InputElementDialog(ed);
		dlg.setCloseOnMouseOut(true);
		dlg.open();
	}

	protected abstract Collection<Object> getAllowedElements();

	protected abstract void doAdd(Object c);

	protected abstract void doRemove(Object m);

	protected abstract Set<Object> getElementGroup(Object el);

	protected abstract void onChange();
}
